import math

#this will be the game object your player will manipulate
class SudokuBoard:

    #the constructor for the SudokuBoard
    def __init__(self, size, board):
        self.size = size #the size of the board
        self.board = board #the current state of the game board
        self.init_const()
        self.init_arcs()

    def __repr__(self):
        print '\n'.join([' '.join(map(str, row)) for row in self.board])

    # This function will create a new sudoku board object with
    # with the input value placed on the GameBoard row and col are
    # both zero-indexed
    def set_value(self, row, col, value):
        self.board[row][col] = value #add the value to the appropriate position on the board
        return SudokuBoard(self.size, self.board) #return a new board of the same size with the value added

    # creates a list with all the initial constraints
    def init_const(self):
        size = self.size
        subsquare = int(math.sqrt(size))
        
        # store no. imposing constraints for each cell
        const = [ [ 0 for col in range(size) ] for row in range(size) ]
        
        # create a list with all possible values for each cell
        allowed_values = [ [ range(1, size + 1) for col in range(size) ] for row in range(size) ]

        for row in range(size):
            for col in range(size):
                # Count no. imposing constraints
                for i in range(size):
                    if self.board[row][i] == 0 and i != col:
                        const[row][col] += 1
                    if self.board[i][col] == 0 and i != row:
                        const[row][col] += 1
                        
                SquareRow = row // subsquare
                SquareCol = col // subsquare
                for i in range(subsquare):
                    for j in range(subsquare):
                        if ((SquareRow * subsquare + i != row) and (SquareCol * subsquare + j != col)
                            and self.board[SquareRow * subsquare + i][SquareCol * subsquare + j] == 0):
                            const[row][col] += 1
                
                # If the cell is already assigned
                if(self.board[row][col] != 0):    
                    # Set allowed values to assigned value
                    allowed_values[row][col] = [self.board[row][col]]
                    
                # If the cell is not assigned
                else:
                    # removes values already present in row and column 
                    for i in range(size):
                        if allowed_values[row][col].count(self.board[row][i]) > 0 and i != col:
                            allowed_values[row][col].remove(self.board[row][i])
                        if allowed_values[row][col].count(self.board[i][col]) > 0 and i != row:
                            allowed_values[row][col].remove(self.board[i][col])
    
                    # removes values already present in square
                    SquareRow = row // subsquare
                    SquareCol = col // subsquare
                    for i in range(subsquare):
                        for j in range(subsquare):
                            if((allowed_values[row][col].count(self.board[SquareRow * subsquare + i][SquareCol * subsquare + j]) > 0)
                               and (SquareRow * subsquare + i != row) and (SquareCol * subsquare + j != col)):
                                    allowed_values[row][col].remove(self.board[SquareRow * subsquare + i][SquareCol * subsquare + j])
                     
        self.const = const
        self.allowed_values = allowed_values

    def init_arcs(self):
        subsquare = int(math.sqrt(self.size))
        arc = []
    
        for row in range(self.size):
            for col in range(self.size):
                for var in range(self.size):
                        if var != row:
                            arc.append(((row, col), (var, col)))
                        if var != col:
                            arc.append(((row, col), (row, var)))
    
                SquareRow = row // subsquare
                SquareCol = col // subsquare
                for i in range(subsquare):
                    for j in range(subsquare):
                        srow = SquareRow * subsquare + i
                        scol = SquareCol * subsquare + j
                        if srow != row and scol != col:
                            arc.append(((row, col), (srow, scol)))
        self.arcs = arc

    def update_const(self, row, col, val):
        subsquare = int(math.sqrt(self.size))
        
        for i in range(self.size):
            # checks row for val
            if self.allowed_values[row][i].count(val) > 0 and i != col:
                self.allowed_values[row][i].remove(val)
                self.const[row][i] -= 1
            # checks col for val    
            if self.allowed_values[i][col].count(val) > 0 and i != row:
                self.allowed_values[i][col].remove(val)
                self.const[i][col] -= 1
            
        SquareRow = row // subsquare
        SquareCol = col // subsquare
        # checks square of [row][col] for val
        for i in range(subsquare):
            for j in range(subsquare):
                if((self.allowed_values[SquareRow * subsquare + i][SquareCol * subsquare + j].count(val) > 0)
                and (SquareRow * subsquare + i != row) and (SquareCol * subsquare + j != col)):
                    self.allowed_values[SquareRow * subsquare + i][SquareCol * subsquare + j].remove(val)
                    self.const[SquareRow * subsquare + i][SquareCol * subsquare + j] -= 1
    
    def update_arcs(self):
        pass

    # Tests if the board has been filled in correctly
    def is_complete(self):
            size = len(self.board)
            subsquare = int(math.sqrt(size))
    
            #check each cell on the board for a 0, or if the value of the cell
            #is present elsewhere within the same row, column, or square
            for row in range(size):
                for col in range(size):
    
                    if self.board[row][col] == 0:
                        return False
                    for i in range(size):
                        if ((self.board[row][i] == self.board[row][col]) and i != col):
                            return False
                        if ((self.board[i][col] == self.board[row][col]) and i != row):
                            return False
                    #determine which square the cell is in
                    SquareRow = row // subsquare
                    SquareCol = col // subsquare
                    for i in range(subsquare):
                        for j in range(subsquare):
                            if((self.board[SquareRow * subsquare + i][SquareCol * subsquare + j] == self.board[row][col])
                               and (SquareRow * subsquare + i != row) and (SquareCol * subsquare + j != col)):
                                return False
            return True
